#include	"lp_global_vars.h"
#include	"config_parser.h"

struct timeval startTime_Total, endTime_Total, performance_start_time, performance_end_time, tv1, tv2;
struct event *print_stat_timeout_eve, *timeoutEv2, *socketEv1;
int learnerID, proposerID;
int lowestDelivered = -1;
int SUBMIT_PER_WAKE, digit = 0, is_proposer;
inst *data;
proposer_msg *p_msgs[MAX_PROPOSERS];
inst *new_data;
const char * config_path;

//------------------------------------------------------------------------------
void initialize_events();
void initialize_buffers();
void validate_parameters(int argc, const char *argv[]);
void clean_exit(int sig);
void enable_ctrl_c_handler();
//------------------------------------------------------------------------------
int main(int argc, const char *argv[]) {
	//---------------------Variables--------------------------------------------
	int status;
	//--------------------------------------------------------------------------
	initialize_events();
	validate_parameters(argc, argv);
	//--------------------------------------------------------------------------
	p_initialize();
	parse_configuration(config_path, -1, proposerID, learnerID);
	enable_ctrl_c_handler();
	//==========================================================================
	initialize_buffers();
	printf("-----------------------------------------------------------\n");
	event_init();
	//---------------------------------Events-----------------------------------
	event_set(socketEv1, get_config_info()->learner_infos[get_config_info()->self_learner_id].self_sid, EV_READ | EV_PERSIST, recv_new_packet_from_predecessor, NULL);
	status = event_add(socketEv1, NULL);
	assert(status >= 0);
	//==========================================================================
	evtimer_set(print_stat_timeout_eve, compute_performance, print_stat_timeout_eve);
	status = evtimer_add(print_stat_timeout_eve, &tv1);
	assert(status >= 0);
	//==========================================================================
	if (is_proposer == 1) {
		evtimer_set(timeoutEv2, generate_proposer_msg_timer_event, timeoutEv2);
		status = evtimer_add(timeoutEv2, &tv2);
		assert(status >= 0);

		//		event_set(timeoutEv2, get_config_info()->learner_infos[get_config_info()->self_learner_id].successor_sid, EV_WRITE | EV_PERSIST, generate_proposer_msg_timer_event, NULL);
		//		status = event_add(timeoutEv2, NULL);
		//		assert(status >= 0);

	}
	//==========================================================================
	event_dispatch();
	exit(0);
}
//------------------------------------------------------------------------------
void initialize_events() {
	tv1.tv_sec = 1;
	tv1.tv_usec = 0;

	socketEv1 = (struct event*) malloc(sizeof(struct event));
	timeoutEv2 = (struct event*) malloc(sizeof(struct event));
	print_stat_timeout_eve = (struct event*) malloc(sizeof(struct event));
}
//------------------------------------------------------------------------------
void initialize_buffers() {

	lp_interval_performance = malloc(sizeof(lp_performance));
	lp_next_performance_index = 0;
	lp_nulify_performance(lp_interval_performance);

	data = (inst*) malloc(sizeof(inst) * (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers));
	new_data = (inst*) malloc(sizeof(inst));

	int i;
	for (i = 0; i < (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers); i++) {
		data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].deliver = false;
		data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].received = false;
		data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].highestDeliverable = -1;
		data[(i % (INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers))].packet.p_msgs[0].msg_id = 0;
	}

	for (i = 0; i < get_config_info()->total_num_proposers; i++)
		p_msgs[i] = (proposer_msg*) malloc(sizeof(proposer_msg) * INTERNAL_BUFFER_SIZE);

	gettimeofday(&startTime_Total, NULL);
	gettimeofday(&performance_start_time, NULL);
}
//------------------------------------------------------------------------------
void validate_parameters(int argc, const char *argv[]) {
	if (argc != 10) {
		printf("Incompatible call to this function. Try Again.(%d)!\n"
			"<1. LearnerID>\n"
			"<2. ProposerID>\n"
			"<3. Config Path>\n"
			"<4. Sending Interval(Microseconds)>\n"
			"<5. Number of packets/Interval (proposing)>\n"
			"<6. File to store the performance>\n"
			"<7. Latency file>\n"
			"<8. SD AVG file>\n"
			"<9. Is this a proposer?(1=y/0=n)>\n", argc);

		exit(-1);
	} else {

		learnerID = atoi(argv[1]);
		proposerID = atoi(argv[2]);
		config_path = argv[3];
		tv2.tv_usec = atoi(argv[4]);
		SUBMIT_PER_WAKE = atoi(argv[5]);
		lp_performance_file = argv[6];
		lp_latency_file = argv[7];
		lp_sd_avg_file = argv[8];
		is_proposer = atoi(argv[9]);
		tv2.tv_sec = 0;

	}
}

void clean_exit(int sig) {

	signal(sig, clean_exit);

	printf("Caught exit signal\n");
	lp_store_performance_in_file(lp_performance_file, lp_latency_file, lp_sd_avg_file, lp_next_performance_index);

	close(get_config_info()->learner_infos[learnerID].self_sid);
	close(get_config_info()->learner_infos[learnerID].successor_sid);

	free(data);
	int i = 0;

	for (i = 0; i < get_config_info()->total_num_proposers; i++)
		free(p_msgs[i]);
	exit(0);

}
void enable_ctrl_c_handler() {
	signal(SIGINT, clean_exit);
	signal(SIGKILL, clean_exit);
	signal(SIGABRT, clean_exit);
	signal(SIGALRM, clean_exit);
	signal(SIGBUS, clean_exit);
	signal(SIGCHLD, clean_exit);
	signal(SIGCONT, clean_exit);
	signal(SIGFPE, clean_exit);
	signal(SIGHUP, clean_exit);
	signal(SIGILL, clean_exit);
	signal(SIGPIPE, clean_exit);
	signal(SIGQUIT, clean_exit);
	signal(SIGSEGV, clean_exit);
	signal(SIGSTOP, clean_exit);
	signal(SIGTERM, clean_exit);
	signal(SIGTSTP, clean_exit);
	signal(SIGTTIN, clean_exit);
	signal(SIGTTOU, clean_exit);
	signal(SIGUSR1, clean_exit);
	signal(SIGUSR2, clean_exit);
	signal(SIGPROF, clean_exit);
	signal(SIGSYS, clean_exit);
	signal(SIGTRAP, clean_exit);
	signal(SIGURG, clean_exit);
	signal(SIGVTALRM, clean_exit);
	signal(SIGXCPU, clean_exit);
	signal(SIGXFSZ, clean_exit);

}
